package org.syntaxlisp.core.element;

import org.syntaxlisp.core.ParseException;
import lombok.Getter;
import org.springframework.beans.BeanUtils;

import java.util.ArrayList;

import static org.syntaxlisp.core.element.SyntaxLispElementType.*;

/**
 * @author HuangYijun
 * @data 2023-03-22 11:44
 **/
public class SimplifyRepeatElement extends SyntaxLispElement {

    @Getter
    private final int minMatchTime;

    @Getter
    private final int maxMatchTime;

    @Getter
    private final boolean isGreedyMatch;

    public static final int INFINITY = -1;

    public SimplifyRepeatElement(SyntaxLispElement src) {
        super(SyntaxLispElementType.SIMPLIFY_REPEAT, null);
        BeanUtils.copyProperties(src, this, "sub");
        setSubElements(new ArrayList<>());
        switch (src.getType()) {
            case REPEAT_GREEDY:
            case REPEAT: {
                minMatchTime = trimNumberVal((NumberElement) src.getSub(0));
                maxMatchTime = trimNumberVal((NumberElement) src.getSub(1));
                getSubElements().add(src.getSub(2));
                isGreedyMatch = (src.getType() == REPEAT_GREEDY);
                break;
            }
            case ONE_OR_MORE_GREEDY:
            case ONE_OR_MORE: {
                minMatchTime = 1;
                maxMatchTime = INFINITY;
                getSubElements().add(src.getSub(0));
                isGreedyMatch = (src.getType() == ONE_OR_MORE_GREEDY);
                break;
            }
            case ZERO_OR_ONE_GREEDY:
            case ZERO_OR_ONE: {
                minMatchTime = 0;
                maxMatchTime = 1;
                getSubElements().add(src.getSub(0));
                isGreedyMatch = (src.getType() == ZERO_OR_ONE_GREEDY);
                break;
            }
            case ZERO_OR_MORE_GREEDY:
            case ZERO_OR_MORE: {
                minMatchTime = 0;
                maxMatchTime = INFINITY;
                getSubElements().add(src.getSub(0));
                isGreedyMatch = (src.getType() == ZERO_OR_MORE_GREEDY);
                break;
            }
            case SIMPLIFY_REPEAT: {
                // for copy
                SimplifyRepeatElement sre = (SimplifyRepeatElement) src;
                minMatchTime = sre.getMinMatchTime();
                maxMatchTime = sre.getMaxMatchTime();
                getSubElements().add(sre.getSub(0).copy());
                isGreedyMatch = sre.isGreedyMatch();
                break;
            }
            default: {
                throw new ParseException("SIMPLIFY_REPEAT can not init from NO-REPEAT element(except REPEAT," +
                                         "ONE_OR_MORE," +
                                         "ZERO_OR_ONE,ZERO_OR_MORE and greedy repeat elements)");
            }
        }
    }

    public SyntaxLispElement getRepeatBody () {
        return getSub(0);
    }

    public void setRepeatBody (SyntaxLispElement repeatBody) {
        setSub(0, repeatBody);
    }

    private int trimNumberVal (NumberElement numberElement) {
        return numberElement.isInfinity()
               ? INFINITY
               : numberElement.getNumVal();
    }

    /**
     * Is the match time exceed the upper limit of the repeat element
     * @param matchTime match time
     * @return
     */
    public boolean exceed(int matchTime) {
        return maxMatchTime != INFINITY && matchTime > maxMatchTime;
    }

    /**
     * Is the match time exactly equals the max match time of the repeat element.
     * @param matchTime match time
     * @return
     */
    public boolean reachMax (int matchTime) {
        return maxMatchTime != INFINITY && matchTime == maxMatchTime;
    }

    /**
     * Is the match time just be in the scope between min match time and max match time of the repeat element.
     * @param matchTime match time
     * @return
     */
    public boolean fit(int matchTime) {
        return matchTime >= minMatchTime && !exceed(matchTime);
    }

    public boolean unReach (int matchTime) {
        return matchTime < minMatchTime;
    }

    @Override
    public SyntaxLispElement copy() {
        return (new SimplifyRepeatElement(this)).copyMatchPropertiesAndReturn(this);
    }

    @Override
    public boolean equalsTo(SyntaxLispElement o) {
        SimplifyRepeatElement rpt = (SimplifyRepeatElement) o;
        return minMatchTime == rpt.getMinMatchTime()
                && maxMatchTime == rpt.getMaxMatchTime()
                && getRepeatBody().equalsTo(rpt.getRepeatBody());
    }
}
